﻿Imports Newtonsoft.Json
Imports System.IO
Imports Sunny.UI
Imports System.Threading
Imports System.Speech.Synthesis
Imports System.Text
Imports NAudio.Wave
Imports Whisper.net
'Imports System.Linq.Async

Public Class Form1
    '公共
    Private currentLib As Library   '加载的教材单词库
    Private currentUnit As Unit   '加载的单元单词库
    Private currentLibAllWords As List(Of Word)   '加载的教材所有单词
    Private currentBasicDict As BasicLibrary   '加载的基础单词库

    Private currentUnitIndex As Integer = 0
    Private currentWordIndex As Integer = 0

    Private ttsEngine As SpeechSynthesizer
    Private SoftName As String = "小手指超级背单词 v2.0"

    Private ErrorsNum As Integer = 0  '记录拼写错误次数，如果超过一定次数，进行提醒
    Private correctAnswer As String ' 存储的正确答案
    ' 录音组件
    Private recorder As WaveInEvent
    Private writer As WaveFileWriter
    Private outputFile As String = "tmpREC.wav"
    Private isRecording As Boolean = False


    ' 语音处理组件
    Private whisperFactory As WhisperFactory
    Private processor As WhisperProcessor
    Private synthesizer As New SpeechSynthesizer()

    ' 评分参数
    Private ReadOnly passingScore As Integer = 60


    ' 定义JSON数据结构
    Public Class Library
        <JsonProperty("library_name")>
        Public Property LibraryName As String
        <JsonProperty("other")>
        Public Property Other As String
        <JsonProperty("units")>
        Public Property Units As List(Of Unit)
    End Class
    '基础词库
    Public Class BasicLibrary
        <JsonProperty("library_name")>
        Public Property LibraryName As String
        <JsonProperty("other")>
        Public Property Other As String
        <JsonProperty("words")>
        Public Property Words As List(Of Word)
    End Class
    Public Class Unit
        <JsonProperty("unit_name")>
        Public Property UnitName As String
        <JsonProperty("theme")>
        Public Property Theme As String
        <JsonProperty("words")>
        Public Property Words As List(Of Word)
    End Class

    Public Class Word
        <JsonProperty("word")>
        Public Property Word As String
        ' 其他字段根据需要添加

        <JsonProperty("phonetic")>
        Public Property Phonetic As String

        <JsonProperty("translation")>
        Public Property Translation As String

        <JsonProperty("en_example")>
        Public Property en_Example As String

        <JsonProperty("cn_example")>
        Public Property cn_Example As String
        <JsonProperty("annotation")>
        Public Property Annotation As String
        <JsonProperty("other")>
        Public Property Other As String
    End Class
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.DoubleBuffered = True
        TabPage1.DoubleBuffered(True)
        TabPage2.DoubleBuffered(True)
        TabPage3.DoubleBuffered(True)
        TabPage4.DoubleBuffered(True)
        TabPage6.DoubleBuffered(True)
        UiTitlePanel1.DoubleBuffered(True)
        UiTitlePanel4.DoubleBuffered(True)
        InitNavMenu()
        InitializeTTS()
        InitBasicDict()

        SingleChrFillTextBox1.Text = "H_ll_o"
        PageHeader1.Text = SoftName

        ' 加载语音模型
        'Try
        whisperFactory = WhisperFactory.FromPath("Models\ggml-base-en.bin") 'ggml-base-en.bin
        processor = whisperFactory.CreateBuilder().WithLanguage("en").Build()
        ' Catch ex As WhisperModelLoadException
        'MessageBox.Show($"模型加载失败: {ex.Message}。请检查：{vbCrLf}1. 模型路径是否正确{vbCrLf}2. 是否安装运行时包")
        ' End Try

        Button1.Left = 0
        Button1.Width = UiTabControlMenu1.ItemSize.Width
        Button1.Top = Me.Height - Button1.Height * 2
        Button2.Left = 0
        Button2.Width = UiTabControlMenu1.ItemSize.Width
        Button2.Top = Me.Height - Button1.Height
        ShowSuccessDialog("关于 小手指背单词", "    小手指背单词，是一款通过手指肌肉记忆及键盘键位背单词软件，背单词速度快、记忆时间长，内置了教材编辑器，可按内置的Excel模板自己做单词教材。" & vbCrLf & "    全部免费开源！无任何收费，如有收费请停止支付！" & vbCrLf & "开源地址：https://gitcode.com/juxina/FingerWords")
    End Sub

    Private Sub InitNavMenu()
        UiNavMenu1.BeginUpdate()
        UiNavMenu1.Nodes.Clear()

        Dim basePath As String = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DictBook")

        If Directory.Exists(basePath) Then
            For Each subFolder In Directory.GetDirectories(basePath)
                AddFolderNode(UiNavMenu1.Nodes, subFolder)
            Next
        End If

        UiNavMenu1.EndUpdate()
    End Sub
    Private Sub InitBasicDict()
        Dim jsonPath As String = "Data\BasicDict.json"
        Try
            Dim tmplib = JsonConvert.DeserializeObject(Of BasicLibrary)(File.ReadAllText(jsonPath))
            currentBasicDict = tmplib
        Catch ex As Exception
            MessageBox.Show($"加载失败: {ex.Message}")
        End Try
    End Sub
    Private Sub AddFolderNode(parentNodes As TreeNodeCollection, folderPath As String)
        Dim node As New TreeNode(Path.GetFileName(folderPath))
        node.Tag = folderPath
        parentNodes.Add(node)

        ' 添加JSON文件节点
        AddJsonNodes(node)

        ' 递归添加子文件夹
        For Each subDir In Directory.GetDirectories(folderPath)
            AddFolderNode(node.Nodes, subDir)
        Next
    End Sub
    Private Sub AddJsonNodes(parentNode As TreeNode)
        Dim jsonFiles = Directory.GetFiles(parentNode.Tag.ToString(), "*.json")
        For Each jsonFile In jsonFiles
            Try
                Dim tmplib = JsonConvert.DeserializeObject(Of Library)(File.ReadAllText(jsonFile))
                If Not String.IsNullOrEmpty(tmplib.LibraryName) Then
                    Dim libNode = New TreeNode(tmplib.LibraryName)

                    ' 存储关联的JSON路径到Tag
                    libNode.Tag = jsonFile

                    ' 添加library_name节点到父节点
                    parentNode.Nodes.Add(libNode)

                    '' 添加units到新的TreeView（这里保持原逻辑）
                    'For Each unit In tmplib.Units
                    '    Dim tempNode = New TreeNode($"{unit.UnitName} - {unit.Theme} (单词数量:{unit.Words.Count})")
                    '    tempNode.Tag = unit
                    '    UiNavMenu2.Nodes.Add(tempNode) ' 旧逻辑可能需要调整
                    'Next
                End If
            Catch
                ' 忽略无效JSON
            End Try
        Next
    End Sub

    Private Sub UiNavMenu1_AfterSelect(sender As Object, e As TreeViewEventArgs) Handles UiNavMenu1.AfterSelect
        If e.Node.Level = 2 AndAlso e.Node.Tag IsNot Nothing Then ' library_name节点
            Dim jsonPath = e.Node.Tag.ToString()
            UiNavMenu2.BeginUpdate()
            UiNavMenu2.Nodes.Clear()

            Try
                Dim tmplib = JsonConvert.DeserializeObject(Of Library)(File.ReadAllText(jsonPath))
                currentLib = tmplib
                For Each unit In tmplib.Units
                    Dim unitNode As New TreeNode($"{unit.UnitName} - {unit.Theme} (单词数量:{unit.Words.Count})")
                    unitNode.Tag = unit ' 可选：存储单元信息用于后续操作
                    UiNavMenu2.Nodes.Add(unitNode)
                Next

                UiNavMenu2.ExpandAll()
            Catch ex As Exception
                MessageBox.Show($"加载失败: {ex.Message}")
            Finally
                UiNavMenu2.EndUpdate()
            End Try
        End If
    End Sub
    Private Sub UiNavMenu2_AfterSelect(sender As Object, e As TreeViewEventArgs) Handles UiNavMenu2.AfterSelect
        If e.Node.Tag IsNot Nothing AndAlso TypeOf e.Node.Tag Is Unit Then
            Dim selectedUnit = CType(e.Node.Tag, Unit)

            Dim sb As String = ""
            For Each word In selectedUnit.Words
                sb = sb & $"{word.Word} - {word.Translation}" & vbCrLf
            Next
            If ShowAskDialog("确认要学习的单词", Microsoft.VisualBasic.Left(sb, Len(sb) - 2)) Then
                currentUnit = selectedUnit

                '加载本教材所有单词
                ' 构建所有单词的列表
                currentLibAllWords = New List(Of Word)()
                For Each unit In currentLib.Units
                    currentLibAllWords.AddRange(unit.Words)
                Next

                ShowSuccessTip("开始学习" & e.Node.Text & "单词吧！")
                'PageHeader1.Text = ($"{SoftName}    当前教材：《{currentLib.LibraryName}》 ⇨ {currentUnit.UnitName} - {currentUnit.Theme}")
                PageHeader1.SubText = ($"当前教材：《{currentLib.LibraryName}》 ⇨ {currentUnit.UnitName} - {currentUnit.Theme}")
                SystemEx.Delay(1500)

                ShowWaitForm("加载数据中...")
                Thread.Sleep(500)
                SetWaitFormDescription("20%")
                Thread.Sleep(500)
                SetWaitFormDescription("40%")
                Thread.Sleep(500)
                SetWaitFormDescription("60%")
                Thread.Sleep(500)
                SetWaitFormDescription("80%")
                Thread.Sleep(500)
                SetWaitFormDescription("100%")
                Thread.Sleep(500)
                HideWaitForm()

                UiTabControlMenu1.SelectedIndex = 1
                currentWordIndex = 0
                单词学习ShowNextWord()

            Else
                'ShowErrorTip("您点击了取消按钮")
            End If
        End If
    End Sub


    ' 初始化语音合成引擎
    Private Sub InitializeTTS()
        ttsEngine = New SpeechSynthesizer()
        ttsEngine.Rate = -2 ' 语速调整（-10到+10）
        ttsEngine.Volume = 100 ' 音量（0-100）
    End Sub

    ' 验证输入逻辑
    Private Sub 单词学习ValidateInput()
        Dim userInput As String = Input1.Text.Trim()
        Dim WordText As String = currentUnit.Words(currentWordIndex).Word
        If String.Equals(userInput, WordText, StringComparison.InvariantCultureIgnoreCase) Then
            Steps1.Current += 1
            ShowSuccessTip("输入正确！")
            ttsEngine.SpeakAsync(",," & WordText & "," & currentUnit.Words(currentWordIndex).Translation & ",,,...")
        Else
            ShowErrorTip("输入错误，请重试。")
        End If

        Input1.Clear()

        If Steps1.Current >= 5 Then
            Steps1.Current = 0
            '判断是否为本单元最后一个单词
            If currentWordIndex >= currentUnit.Words.Count - 1 Then
                ShowSuccessTip("本单元单词已学习完毕！")
                SystemEx.Delay(1500)
                If ShowAskDialog("是否进入下一环节？") Then
                    UiTabControlMenu1.SelectedIndex = 2
                End If
            Else
                currentWordIndex += 1
                单词学习ShowNextWord()
            End If
        End If

    End Sub
    ' 单词学习加载下一单词
    Private Sub 单词学习ShowNextWord()
        Input1.Clear()
        If currentUnit Is Nothing OrElse currentUnit.Words.Count = 0 Then Return

        Dim currentWord = currentUnit.Words(currentWordIndex)

        ' 显示单词详细信息
        LabWord.Text = currentWord.Word
        LabPho.Text = currentWord.Phonetic
        LabTra.Text = currentWord.Translation
        Laben.Text = currentWord.en_Example
        Labcn.Text = currentWord.cn_Example
        Label16.Text = "当前：" & currentWordIndex + 1 & " / " & currentUnit.Words.Count

        ' 开始朗读（异步执行)循环5边
        If ttsEngine IsNot Nothing Then
            ttsEngine.Dispose()
        End If
        InitializeTTS()
        For i As Integer = 1 To 2
            ttsEngine.SpeakAsync(",," & currentWord.Word & "," & currentWord.Translation & ",,,...")
        Next
    End Sub
    ' 拼写巩固加载下一单词
    Private Sub 拼写巩固ShowNextWord(order As Integer)
        ErrorsNum = 0
        If currentUnit Is Nothing OrElse currentUnit.Words.Count = 0 Then Return

        Dim currentWord = currentUnit.Words(currentWordIndex)

        Me.SuspendLayout()
        ' 显示单词详细信息
        Label18.Text = "当前：" & currentWordIndex + 1 & " / " & currentUnit.Words.Count
        Label6.Text = currentWord.Translation
        SingleChrFillTextBox1.CharCount = Len(currentWord.Word)
        Dim tmpWord As String = currentWord.Word

        tmpWord = ReplaceLetters(currentWord.Word, 1 - order * 0.2)

        SingleChrFillTextBox1.Text = tmpWord
        SingleChrFillTextBox1.FocusFirstEditableTextBox()

        Me.ResumeLayout(True)

        If ttsEngine IsNot Nothing Then
            ttsEngine.Dispose()
        End If
        InitializeTTS()
        ttsEngine.SpeakAsync(",," & currentWord.Word & "," & currentWord.Translation)
    End Sub
    ' 拼写巩固验证输入逻辑
    Private Sub 拼写巩固ValidateInput(tmpStr As String)
        Dim userInput As String = tmpStr
        Dim WordText As String = currentUnit.Words(currentWordIndex).Word
        If String.Equals(userInput, WordText, StringComparison.InvariantCultureIgnoreCase) Then
            Steps2.Current += 1
            ErrorsNum = 0
            ShowSuccessTip("输入正确！")
            ttsEngine.SpeakAsync(",," & WordText & "," & currentUnit.Words(currentWordIndex).Translation)
            拼写巩固ShowNextWord(Steps2.Current + 1)
        Else
            ErrorsNum += 1
            If ErrorsNum >= 3 Then
                ShowDict()
                ErrorsNum = 0
            End If
            ShowErrorTip("输入错误，请重试。")
        End If

        'SingleChrFillTextBox1.ClearText()
        Me.SuspendLayout()

        SingleChrFillTextBox1.Text = SingleChrFillTextBox1.Text
        SingleChrFillTextBox1.FocusFirstEditableTextBox()

        Me.ResumeLayout(True)

        If Steps2.Current >= 5 Then
            Steps2.Current = 0
            '判断是否为本单元最后一个单词
            If currentWordIndex >= currentUnit.Words.Count - 1 Then
                ShowSuccessTip("本单元单词已学习完毕！")
                SystemEx.Delay(1500)
                If ShowAskDialog("是否进入下一环节？") Then
                    UiTabControlMenu1.SelectedIndex = 4
                End If
            Else
                currentWordIndex += 1
                拼写巩固ShowNextWord(1)
            End If
        End If

    End Sub
    ' 词义掌握加载下一单词
    Private Sub 词义掌握ShowNextWord()
        If currentUnit Is Nothing OrElse currentUnit.Words.Count = 0 Then Return
        Dim rnd As New Random()
        Dim currentWord = currentUnit.Words(currentWordIndex)
        Dim options As New List(Of String)
        Label17.Text = "当前：" & currentWordIndex + 1 & " / " & currentUnit.Words.Count
        ' 显示单词详细信息
        If Steps3.Current >= 3 Then   '看词意选单词
            Label10.Text = "词意"
            Label11.Text = currentWord.Translation
            Label12.Text = currentWord.Phonetic
            Label14.Text = currentWord.cn_Example
            correctAnswer = currentWord.Word
            options.Add(currentWord.Word) ' 正确答案
            ' 添加3个随机干扰项（确保不重复）
            While options.Count < 4
                Dim randomIndex = rnd.Next(currentLibAllWords.Count)
                Dim randomWord = currentLibAllWords(randomIndex).Word

                ' 排除正确答案和重复项
                If randomWord <> currentWord.Word AndAlso
                   Not options.Contains(randomWord) Then
                    options.Add(randomWord)
                End If
            End While
        Else '看单词选词意
            Label10.Text = "单词"
            Label11.Text = currentWord.Word
            Label12.Text = currentWord.Phonetic
            Label14.Text = currentWord.en_Example
            correctAnswer = currentWord.Translation
            ' 创建选项列表（1个正确答案 + 3个随机干扰项）
            options.Add(currentWord.Translation) ' 正确答案

            ' 添加3个随机干扰项（确保不重复）
            While options.Count < 4
                Dim randomIndex = rnd.Next(currentLibAllWords.Count)
                Dim randomTranslation = currentLibAllWords(randomIndex).Translation

                Dim dotIndex As Integer = randomTranslation.IndexOf("."c)
                If dotIndex >= 0 Then
                    randomTranslation = randomTranslation.Substring(dotIndex + 1).TrimStart()
                End If

                ' 排除正确答案和重复项
                If randomTranslation <> currentWord.Translation AndAlso
                   Not options.Contains(randomTranslation) Then
                    options.Add(randomTranslation)
                End If
            End While

        End If

        ' 随机打乱选项顺序
        ShuffleOptions(options)

        ' 分配给RadioButton\AntdUI.Radio
        Dim sButton As UISymbolButton() = {UiSymbolButton1, UiSymbolButton2, UiSymbolButton3, UiSymbolButton4}
        For i As Integer = 0 To 3
            sButton(i).Text = options(i)
            sButton(i).Tag = options(i) ' 存储选项文本到Tag属性
            sButton(i).Style = UIStyle.Inherited
            sButton(i).Symbol = 0
        Next

        ' 开始朗读（异步执行)循环5边
        If ttsEngine IsNot Nothing Then
            ttsEngine.Dispose()
        End If
        InitializeTTS()
        If Steps3.Current >= 3 Then
            ttsEngine.SpeakAsync(",," & currentWord.Translation)
        Else
            ttsEngine.SpeakAsync(",," & currentWord.Word)
        End If

    End Sub


    Private Async Sub ShowDict()   '输入错误次数过多时，提醒3秒
        Label8.Text = currentUnit.Words(currentWordIndex).Word
        Label7.Visible = True
        Label8.Visible = True
        Await Task.Delay(5000)
        Label7.Visible = False
        Label8.Visible = False
    End Sub
    ' 窗体关闭时释放资源
    Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
        If ttsEngine IsNot Nothing Then
            ttsEngine.Dispose()
        End If
        MyBase.OnFormClosing(e)
    End Sub


    Private Sub Input1_KeyDown(sender As Object, e As KeyEventArgs) Handles Input1.KeyDown
        If e.KeyCode = Keys.Enter Then
            单词学习ValidateInput()
            e.Handled = True
            e.SuppressKeyPress = True
        End If
    End Sub


    Private Sub LabWord_Click(sender As Object, e As EventArgs) Handles LabWord.Click
        ttsEngine.SpeakAsync(currentUnit.Words(currentWordIndex).Word)
    End Sub

    Private Sub LabPho_Click(sender As Object, e As EventArgs) Handles LabPho.Click
        ttsEngine.SpeakAsync(currentUnit.Words(currentWordIndex).Word)
    End Sub

    Private Sub LabTra_Click(sender As Object, e As EventArgs) Handles LabTra.Click
        ttsEngine.SpeakAsync(currentUnit.Words(currentWordIndex).Translation)
    End Sub

    Private Sub Laben_Click(sender As Object, e As EventArgs) Handles Laben.Click
        ttsEngine.SpeakAsync(currentUnit.Words(currentWordIndex).en_Example)
    End Sub

    Private Sub Labcn_Click(sender As Object, e As EventArgs) Handles Labcn.Click
        ttsEngine.SpeakAsync(currentUnit.Words(currentWordIndex).cn_Example)
    End Sub

    Private Function ReplaceLetters(inputString As String, percentage As Double) As String
        Dim rnd As New Random()
        percentage = Math.Max(0, Math.Min(1, percentage))

        Dim result As New StringBuilder(inputString.Length)
        Dim letterIndices As New List(Of Integer) ' 存储字母的原始索引
        Dim hasReplacement As Boolean = False
        For i As Integer = 0 To inputString.Length - 1
            Dim c As Char = inputString(i)
            ' 检查是否为英文字母
            If (c >= "A"c AndAlso c <= "Z"c) OrElse (c >= "a"c AndAlso c <= "z"c) Then
                letterIndices.Add(i) ' 记录字母位置
                ' 根据概率决定是否替换
                If rnd.NextDouble() <= percentage Then
                    result.Append(c) ' 保留原字符
                Else
                    result.Append("_"c) ' 替换为下划线
                    hasReplacement = True
                End If
            Else
                result.Append(c) ' 非字母字符直接保留
            End If
        Next
        ' 若未替换且存在字母，强制替换一个
        If Not hasReplacement AndAlso letterIndices.Count > 0 Then
            Dim index As Integer = rnd.Next(letterIndices.Count)
            result(letterIndices(index)) = "_"c
        End If
        Return result.ToString()
    End Function

    Private Sub Label6_Click(sender As Object, e As EventArgs) Handles Label6.Click
        ttsEngine.SpeakAsync(currentUnit.Words(currentWordIndex).Word & "," & currentUnit.Words(currentWordIndex).Translation)
    End Sub


    Private Sub UiTabControlMenu1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles UiTabControlMenu1.SelectedIndexChanged
        If currentUnit Is Nothing Then
            UiTabControlMenu1.SelectedIndex = 0
            ShowErrorTip("请先选择教材！")
            Return
        Else
            Select Case sender.SelectedIndex
                Case 0
                    InitNavMenu()
                Case 1
                    currentWordIndex = 0
                    单词学习ShowNextWord()
                Case 2
                    currentWordIndex = 0
                    词义掌握ShowNextWord()
                Case 3
                    currentWordIndex = 0
                    拼写巩固ShowNextWord(1)
                Case 4
                    currentWordIndex = 0
                    语音练习ShowNextWord()
            End Select
        End If

    End Sub

    Private Sub SingleChrFillTextBox1_KeyUp(sender As Object, e As KeyEventArgs) Handles SingleChrFillTextBox1.KeyUp
        If SingleChrFillTextBox1.GetText.Contains("_"c) = False Then
            拼写巩固ValidateInput(SingleChrFillTextBox1.GetText)
        End If
    End Sub

    ' Fisher-Yates洗牌算法
    Private Sub ShuffleOptions(list As List(Of String))
        Dim rnd As New Random()
        Dim n = list.Count
        While n > 1
            n -= 1
            Dim k = rnd.Next(n + 1)
            Dim value = list(k)
            list(k) = list(n)
            list(n) = value
        End While
    End Sub
    ' 词义掌握验证输入逻辑
    Private Sub UiSymbolButton1to4_Click(sender As Object, e As EventArgs) Handles UiSymbolButton1.Click, UiSymbolButton2.Click, UiSymbolButton3.Click, UiSymbolButton4.Click
        Dim clickedButton As UISymbolButton = DirectCast(sender, UISymbolButton)
        词义掌握ValidateInput(clickedButton)
    End Sub

    ' 词义掌握验证输入逻辑
    Private Async Sub 词义掌握ValidateInput(clickedButton As UISymbolButton)
        Dim selectedAnswer As String = clickedButton.Tag.ToString()

        If selectedAnswer = correctAnswer Then
            'For Each btn As UISymbolButton In {UiSymbolButton1, UiSymbolButton2, UiSymbolButton3, UiSymbolButton4}
            '    If btn.Tag.ToString() = correctAnswer Then
            '        btn.Style = UIStyle.Green
            '        btn.Symbol = 61452
            '    Else
            '        btn.Style = UIStyle.Red
            '        btn.Symbol = 61453
            '    End If
            'Next
            clickedButton.Style = UIStyle.Green
            clickedButton.Symbol = 61452
            Steps3.Current += 1
            ShowSuccessTip("选择正确！")
            Await Task.Delay(1500)
        Else
            clickedButton.Style = UIStyle.Red
            clickedButton.Symbol = 61453
            ShowErrorTip("选择错误，请重试。")
            Await Task.Delay(1500)
        End If

        If Steps3.Current >= 6 Then
            Steps3.Current = 0
            '判断是否为本单元最后一个单词
            If currentWordIndex >= currentUnit.Words.Count - 1 Then
                ShowSuccessTip("本单元单词已学习完毕！")
                SystemEx.Delay(1500)
                If ShowAskDialog("是否进入下一环节？") Then
                    UiTabControlMenu1.SelectedIndex = 3
                End If
            Else
                currentWordIndex += 1
                词义掌握ShowNextWord()
            End If
        Else

            词义掌握ShowNextWord()
        End If

    End Sub
    ' 语音练习加载下一单词
    Private Sub 语音练习ShowNextWord()

        If currentUnit Is Nothing OrElse currentUnit.Words.Count = 0 Then Return

        Dim currentWord = currentUnit.Words(currentWordIndex)

        ' 显示单词详细信息
        Label21.Text = currentWord.Word
        Label22.Text = currentWord.Phonetic
        Label20.Text = currentWord.Translation
        Label26.Text = ""
        Label27.Text = ""
        Rate1.Value = 0
        Label25.Text = "当前：" & currentWordIndex + 1 & " / " & currentUnit.Words.Count

        ' 开始朗读（异步执行)循环5边
        If ttsEngine IsNot Nothing Then
            ttsEngine.Dispose()
        End If
        InitializeTTS()

        ttsEngine.SpeakAsync(",," & currentWord.Word）
        Label26.Text = ""
    End Sub

    Private Sub UiSymbolButton5_MouseDown(sender As Object, e As MouseEventArgs) Handles UiSymbolButton5.MouseDown
        Progress1.Value = 0 ' 重置音量条
        Try
            ' 配置录音
            recorder = New WaveInEvent With {
                .WaveFormat = New WaveFormat(16000, 16, 1)
            }
            writer = New WaveFileWriter(outputFile, recorder.WaveFormat)
            isRecording = True
            AddHandler recorder.DataAvailable, AddressOf Recorder_DataAvailable
            AddHandler recorder.RecordingStopped, AddressOf Recorder_RecordingStopped
            UiSymbolButton5.Text = "录音中..."
            recorder.StartRecording()
        Catch ex As Exception
            ShowErrorTip($"录音启动失败: {ex.Message}")
        End Try

    End Sub

    Private Async Sub UiSymbolButton5_MouseUp(sender As Object, e As MouseEventArgs) Handles UiSymbolButton5.MouseUp
        Try
            Progress1.Value = 0 ' 重置音量条
            isRecording = False
            recorder.StopRecording()
            recorder.Dispose()
            recorder = Nothing

            ' 关闭文件
            writer.Close()
            writer = Nothing
            UiSymbolButton5.Text = "识别中"

            ' 语音识别
            Dim recognizedtext As String = ""
            ' 确保文件写入完成
            'await task.delay(500)

            ' 1. 检查文件是否存在
            If Not File.Exists(outputFile) Then
                ShowErrorTip("识别失败：未为检测到录音！")
            Else
                ' 2. 检查文件大小
                Dim fileinfo = New FileInfo(outputFile)
                If fileinfo.Length < 512 Then ' 小于1kb
                    ShowErrorTip("识别失败：录音文件短于0.5kb！")
                Else
                    Using filestream = File.OpenRead(outputFile)
                        'try
                        Dim segments = processor.ProcessAsync(filestream)
                        Dim resulttext As New StringBuilder()

                        ' 使用正确的 segmentdata 类型
                        For Each segment As SegmentData In Await segments.ToListAsync()
                            resulttext.AppendLine(segment.Text)
                        Next

                        recognizedtext = resulttext.ToString()

                        ' 计算评分
                        Dim score = CalculateScore(recognizedtext, currentUnit.Words(currentWordIndex).Word, outputFile)

                        ' 显示结果
                        DisplayResult(score, recognizedtext)
                        'catch ex as exception

                        ' finally


                        'end try
                    End Using
                End If
            End If

            Label26.Text = "识别结果：" & recognizedtext

        Catch ex As Exception
            ShowErrorTip($"识别失败: {ex.Message}")
        Finally
            ' 清理临时文件
            Try
                If File.Exists(outputFile) Then File.Delete(outputFile)
            Catch
                ' 忽略删除错误
            End Try
            UiSymbolButton5.Text = "按住录音"
        End Try

    End Sub

    ' 评分算法
    Private Function CalculateScore(recognizedText As String, targetText As String, audioPath As String) As Integer
        ' 步骤1：预处理文本（忽略大小写和标点）
        Dim cleanRecognized = CleanText(recognizedText)
        Dim cleanTarget = CleanText(targetText)
        Dim isPass As Boolean = False
        ' 步骤2：基础分判断（核心改进）
        Dim baseScore As Integer
        If cleanRecognized = cleanTarget Then
            ' 识别完全匹配，基础分60分
            baseScore = 60
            isPass = True
        Else
            Dim dis As Integer = CalculateLevenshteinDistance(cleanRecognized, cleanTarget)
            If dis >= 20 Then
                ' 近似匹配（如welcom→welcome），基础分40分
                baseScore = dis
                isPass = True
            Else
                ' 完全不匹配，基础分0分
                baseScore = dis
                isPass = False
            End If
        End If


        ' 步骤3：发音质量评分（0-40分）
        Dim pronunciationScore As Integer = 0
        If isPass Then ' 仅当有匹配时评估发音
            pronunciationScore = CalculatePronunciationScore(audioPath, cleanTarget)
        End If

        ' 步骤4：总分计算（基础分 + 发音分）
        Dim totalScore = Math.Min(100, baseScore + pronunciationScore)

        ' 确保匹配时至少60分
        If cleanRecognized = cleanTarget AndAlso totalScore < 60 Then
            totalScore = 60
        End If

        Return totalScore
    End Function

    ' 计算编辑距离（Levenshtein距离）
    Private Function CalculateLevenshteinDistance(a As String, b As String) As Integer
        ' 处理空字符串的特殊情况
        If String.IsNullOrEmpty(a) AndAlso String.IsNullOrEmpty(b) Then
            Return 59 ' 两个空字符串视为完全匹配
        ElseIf String.IsNullOrEmpty(a) OrElse String.IsNullOrEmpty(b) Then
            Return 0 ' 一个空一个非空视为完全不匹配
        End If

        Dim matrix(a.Length, b.Length) As Integer

        For i = 0 To a.Length
            matrix(i, 0) = i
        Next

        For j = 0 To b.Length
            matrix(0, j) = j
        Next

        For i = 1 To a.Length
            For j = 1 To b.Length
                Dim cost = If(a(i - 1) = b(j - 1), 0, 1)
                matrix(i, j) = Math.Min(Math.Min(
                matrix(i - 1, j) + 1,    ' 删除
                matrix(i, j - 1) + 1),   ' 插入
                matrix(i - 1, j - 1) + cost) ' 替换
            Next
        Next

        Dim distance As Integer = matrix(a.Length, b.Length)

        ' 获取最大可能距离（较长字符串的长度）
        Dim maxLength As Integer = Math.Max(a.Length, b.Length)

        ' 将距离映射到0-59分范围
        ' 距离为0（完全匹配）-> 59分
        ' 距离为maxLength（完全不匹配）-> 0分
        Dim score As Double = 59 * (1 - distance / maxLength)

        ' 确保分数在0-40范围内
        If score < 0 Then score = 0
        If score > 59 Then score = 59

        Return CInt(Math.Round(score))
    End Function
    ' 显示结果
    Private Sub DisplayResult(score As Integer, recognizedText As String)

        Rate1.Value = score / 10
        ' 语音反馈
        Dim prompt As String
        If score >= passingScore Then
            If score >= 80 Then
                prompt = "你好棒！"
                Rate1.Fill = Color.Green
            Else
                prompt = "还可以！"
                Rate1.Fill = Color.Yellow
            End If
            Steps4.Current += 1
        Else
            prompt = "不太好，再努力！"
            Rate1.Fill = Color.Red
        End If

        Label27.Text = $"评分: {score}/100  {prompt}"
        synthesizer.SpeakAsync(prompt)

        If Steps4.Current >= 3 Then
            Steps4.Current = 0
            '判断是否为本单元最后一个单词
            If currentWordIndex >= currentUnit.Words.Count - 1 Then
                ShowSuccessTip("本单元单词已学习完毕！")
                SystemEx.Delay(1500)
                If currentUnitIndex < currentLib.Units.Count Then

                    If ShowAskDialog("是否进入下一单元？") Then
                        currentUnit = currentLib.Units(currentUnitIndex + 1)
                        ShowSuccessTip("开始学习" & currentUnit.UnitName & "单词吧！")
                        'PageHeader1.Text = ($"{SoftName}    当前教材：《{currentLib.LibraryName}》 ⇨ {currentUnit.UnitName} - {currentUnit.Theme}")
                        PageHeader1.SubText = ($"当前教材：《{currentLib.LibraryName}》 ⇨ {currentUnit.UnitName} - {currentUnit.Theme}")
                        SystemEx.Delay(1500)

                        ShowWaitForm("加载数据中...")
                        Thread.Sleep(500)
                        SetWaitFormDescription("20%")
                        Thread.Sleep(500)
                        SetWaitFormDescription("40%")
                        Thread.Sleep(500)
                        SetWaitFormDescription("60%")
                        Thread.Sleep(500)
                        SetWaitFormDescription("80%")
                        Thread.Sleep(500)
                        SetWaitFormDescription("100%")
                        Thread.Sleep(500)
                        HideWaitForm()

                        UiTabControlMenu1.SelectedIndex = 1
                        currentWordIndex = 0
                        单词学习ShowNextWord()
                    End If
                Else
                    ShowSuccessTip("本教材已全部学习完毕，请重新选择教材！")
                    SystemEx.Delay(1500)
                    UiTabControlMenu1.SelectedIndex = 0
                End If
            Else
                currentWordIndex += 1
                SystemEx.Delay(1000)
                语音练习ShowNextWord()
                Label26.Text = ""
            End If
        End If

    End Sub

    ' 文本清洗函数（移除标点、转换为小写）
    Private Function CleanText(text As String) As String
        If String.IsNullOrEmpty(text) Then Return ""

        ' 移除所有标点符号
        Dim cleaned = text.ToLower().Trim()
        Dim sb As New StringBuilder()
        For Each c In cleaned
            If Not Char.IsPunctuation(c) Then
                sb.Append(c)
            End If
        Next

        Return sb.ToString()
    End Function


    Private Function GetAudioDuration(path As String) As TimeSpan
        Using reader = New AudioFileReader(path)
            Return reader.TotalTime
        End Using
    End Function
    ' 发音质量评分（0-40分）
    Private Function CalculatePronunciationScore(audioPath As String, targetWord As String) As Integer
        Dim score = 0

        ' 1. 语速评分（0-15分）
        Dim duration = GetAudioDuration(audioPath)
        Dim idealDuration = targetWord.Length * 0.5 ' 每个字母0.5秒
        Dim timeDiff = Math.Abs(duration.TotalSeconds - idealDuration)
        score += CInt(Math.Max(0, 15 - timeDiff * 5))

        ' 2. 音量稳定性（0-10分）
        score += CalculateVolumeStability(audioPath)

        ' 3. 语调准确度（0-15分）
        score += CalculatePitchAccuracy(audioPath, targetWord)

        Return Math.Min(40, score)
    End Function
    ' 音量稳定性分析
    Private Function CalculateVolumeStability(audioPath As String) As Integer
        Using reader = New AudioFileReader(audioPath)
            Dim buffer(1023) As Single
            Dim readSamples As Integer
            Dim volumeSum As Double = 0
            Dim sampleCount As Integer = 0

            Do
                readSamples = reader.Read(buffer, 0, buffer.Length)
                For i = 0 To readSamples - 1
                    volumeSum += Math.Abs(buffer(i))
                Next
                sampleCount += readSamples
            Loop While readSamples > 0

            Dim avgVolume = volumeSum / sampleCount

            ' 重新读取计算标准差
            reader.Position = 0
            Dim varianceSum As Double = 0
            Do
                readSamples = reader.Read(buffer, 0, buffer.Length)
                For i = 0 To readSamples - 1
                    varianceSum += Math.Pow(Math.Abs(buffer(i)) - avgVolume, 2)
                Next
            Loop While readSamples > 0

            Dim stdDev = Math.Sqrt(varianceSum / sampleCount)

            ' 标准差越小分数越高
            Return CInt(Math.Max(0, 10 - stdDev * 20))
        End Using
    End Function

    ' 语调准确度分析（简化版）
    Private Function CalculatePitchAccuracy(audioPath As String, targetWord As String) As Integer
        ' 实际应用中应使用专业音频分析库
        ' 这里使用随机分数模拟（真实系统应分析基频曲线）
        Dim rnd As New Random()
        Return rnd.Next(8, 15) ' 返回8-15分的随机值
    End Function
    Private Sub Recorder_DataAvailable(sender As Object, e As WaveInEventArgs)
        If isRecording = False Then Return

        If writer IsNot Nothing Then
            ' 计算最大振幅值
            Dim maxAmplitude As Single = 0
            For i As Integer = 0 To e.BytesRecorded - 1 Step 2
                Dim sample As Short = BitConverter.ToInt16(e.Buffer, i)
                Dim amplitude As Single = Math.Abs(sample / 32768.0F) ' 16位音频归一化
                If amplitude > maxAmplitude Then maxAmplitude = amplitude * 10
            Next
            If maxAmplitude > 1 Then maxAmplitude = 1
            ' 更新进度条（跨线程安全）
            Me.Invoke(Sub()
                          Progress1.Value = CDec(maxAmplitude) ' 转换为百分比
                      End Sub)

            writer.Write(e.Buffer, 0, e.BytesRecorded)  ' 写入音频数据
            ' 可选：限制录音时长（例如30秒自动停止）
            If writer.Position > recorder.WaveFormat.AverageBytesPerSecond * 30 Then
                recorder.StopRecording()
            End If
        End If
    End Sub
    Private Sub Recorder_RecordingStopped(sender As Object, e As StoppedEventArgs)
        If writer IsNot Nothing Then
            writer.Dispose()  ' 释放文件写入器
            writer = Nothing
        End If
    End Sub

    Private Sub Label21_Click(sender As Object, e As EventArgs) Handles Label21.Click
        ttsEngine.SpeakAsync(currentUnit.Words(currentWordIndex).Word)
    End Sub

    Private Sub Label22_Click(sender As Object, e As EventArgs) Handles Label22.Click
        ttsEngine.SpeakAsync(currentUnit.Words(currentWordIndex).Word)
    End Sub

    Private Sub Label20_Click(sender As Object, e As EventArgs) Handles Label20.Click
        ttsEngine.SpeakAsync(currentUnit.Words(currentWordIndex).Translation)
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Form2.Show()
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        ShowSuccessDialog("关于 小手指背单词", "    小手指背单词，是一款通过手指肌肉记忆及键盘键位背单词软件，背单词速度快、记忆时间长，内置了教材编辑器，可按内置的Excel模板自己做单词教材。" & vbCrLf & "    全部免费开源！无任何收费，如有收费请停止支付！" & vbCrLf & "开源地址：https://gitcode.com/juxina/FingerWords")
    End Sub
End Class
